﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Threading;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Diagnostics;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.StorageClient;
using AgentWorker.Model;
using Microsoft.AppFabricCAT.Samples.Azure.TransientFaultHandling;
using Microsoft.AppFabricCAT.Samples.Azure.TransientFaultHandling.SqlAzure;
using System.Configuration;
using System.Data.SqlClient;
using System.Data.Objects;
namespace AgentWorker
{
    public class WorkerRole : RoleEntryPoint
    {
        public override void Run()
        {
            // This is a sample worker implementation. Replace with your logic.
            Trace.WriteLine("AgentWorker entry point called", "Information");
            
            while (true)
            {
                // WWB: Clean Up The Unfinished Jobs
                CleanUp();
                DateTime nextExecutionTime = new DateTime(
                    DateTime.UtcNow.Year, 
                    DateTime.UtcNow.Month, 
                    DateTime.UtcNow.Day,
                    3, 0, 0);
                if (DateTime.UtcNow > nextExecutionTime)
                {
                    using (Model.Entities ctx = new Model.Entities())
                    {
                        foreach (var job in ctx.vwJobsToExecutes)
                        {
                            // WWB: After 1:00 pm, Try to Get a Job Id.
                            Guid? jobId = StartJob(job);
                            if (jobId.HasValue)
                            {
                                Trace.WriteLine("Working", "Information");
                                // WWB: This Method Has the Code That Execute
                                // A Stored Procedure, The Actual Job
                                ExecuteJob(job);
                                StopJob(jobId.Value);
                            }
                        }
                    }
                    // WWB: Sleep For An Hour
                    // This Reduces The Calls To StartJob
                    //Thread.Sleep(3600000);
                    Thread.Sleep(20000);
                }
                else
                {
                    // WWB: Check Every Minute
                    Thread.Sleep(2000);
                }
            }
        }

        public override bool OnStart()
        {
            // Set the maximum number of concurrent connections 
            ServicePointManager.DefaultConnectionLimit = 12;

            // For information on handling configuration changes
            // see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.

            return base.OnStart();
        }

        #region JobMethods
        #region DefaultConnectionString
        private string DefaultConnectionString()
        {
            return ConfigurationManager
                .ConnectionStrings["DefaultSqlAzure"]
                .ConnectionString;
        }
        #endregion

        #region ExecuteJob
        private void ExecuteJob(vwJobsToExecute job)
        {
            string connString =
                (string.IsNullOrEmpty(job.ConnectionStringToUse))
                ? DefaultConnectionString()
                : job.ConnectionStringToUse;
            
            using (var con = new SqlConnection(connString))
            {
                try
                {
                    con.Open();
                    using (var cmd = con.CreateCommand())
                    {
                        cmd.CommandText = job.SqlToExecute;
                        cmd.CommandType = System.Data.CommandType.StoredProcedure;
                        cmd.ExecuteNonQuery();
                    }
                }
                catch (SqlException)
                {
                    Trace.TraceError("SqlException");
                    throw;
                }
            }
        }
        #endregion

        #region StartJob
        private Guid? StartJob(vwJobsToExecute job)
        {
            Guid? retValue = null;
            using (Entities ctx = new Entities())
            {
                var activityIdParam = 
                    new ObjectParameter("ActivityId", typeof(Guid?));
                ctx.StartJob(job.JobId, activityIdParam);
                if (activityIdParam.Value != null 
                    && activityIdParam.Value.GetType() 
                    != typeof(System.DBNull))
                {
                    retValue = (Guid)activityIdParam.Value;
                }
            }
            return retValue;
        }
        #endregion

        #region StopJob
        private void StopJob(Guid activityId)
        {
            var rPolicy = new RetryPolicy<SqlAzureTransientErrorDetectionStrategy>
                (5, TimeSpan.FromMilliseconds(150));
            using (ReliableSqlConnection con = 
                new ReliableSqlConnection(DefaultConnectionString(), rPolicy))
            {
                con.Open();
                using (var cmdStop = con.CreateCommand())
                {
                    cmdStop.CommandType = System.Data.CommandType.StoredProcedure;
                    cmdStop.CommandText = "StopJob";
                    cmdStop.Parameters.AddWithValue("@ActivityId", activityId);
                    con.ExecuteCommand(cmdStop, rPolicy);
                }
            }
        }
        #endregion

        #region CleanUp
        private void CleanUp()
        {
            using (Entities ctx = new Entities())
            {
                ctx.CleanUp();
            }
        }
        #endregion
        #endregion
    }
}
